import tkinter as tk
from tkinterdnd2 import TkinterDnD, DND_FILES
import zlib
from tkinter import ttk
import threading
import concurrent.futures
from Crypto.Hash import SHA1, SHA224, SHA256, SHA384, SHA512, MD5

# 全局变量
BUFFER_SIZE = 64 * 1024
UPDATE_INTERVAL = 200 * 1024 * 1024
MAIN_WINDOW_SIZE = "560x370"
TEXT_FIELD_WIDTH = 60
TEXT_FIELD_HEIGHT = 13
CHECKBOX_PADDING = 3  # 复选框之间的间距

def update_progress(value):
    progress_bar["value"] = value * 100
    root.update_idletasks()

def calculate_hash(file_path, hash_function, progress_callback, update_interval=UPDATE_INTERVAL):
    hash_obj = hash_function.new()
    with open(file_path, "rb") as f:
        total_size = f.seek(0, 2)  # 获取文件总大小
        f.seek(0)  # 回到文件开头
        bytes_processed = 0
        last_update = 0
        for byte_block in iter(lambda: f.read(BUFFER_SIZE), b""):
            hash_obj.update(byte_block)
            bytes_processed += len(byte_block)
            if bytes_processed - last_update >= update_interval:
                progress_callback(bytes_processed / total_size)
                last_update = bytes_processed
    progress_callback(1.0)  # 确保进度条在完成后满载
    return hash_obj.hexdigest()

def calculate_crc32(file_path, progress_callback, update_interval=UPDATE_INTERVAL):
    crc32_value = 0
    with open(file_path, "rb") as f:
        total_size = f.seek(0, 2)  # 获取文件总大小
        f.seek(0)  # 回到文件开头
        bytes_processed = 0
        last_update = 0
        for byte_block in iter(lambda: f.read(BUFFER_SIZE), b""):
            crc32_value = zlib.crc32(byte_block, crc32_value)
            bytes_processed += len(byte_block)
            if bytes_processed - last_update >= update_interval:
                progress_callback(bytes_processed / total_size)
                last_update = bytes_processed
    progress_callback(1.0)  # 确保进度条在完成后满载
    return format(crc32_value & 0xFFFFFFFF, '08x')

def insert_colored_text(text_widget, label, hash_value):
    text_widget.config(state=tk.NORMAL)
    text_widget.insert(tk.END, f"{label}: ", "highlight")
    text_widget.insert(tk.END, f"{hash_value}\n", "normal")
    text_widget.config(state=tk.DISABLED)

def calculate_all_hashes(file_path):
    # 清空结果区域
    result_text.config(state=tk.NORMAL)
    result_text.delete(1.0, tk.END)

    # 显示文件路径
    result_text.insert(tk.END, f"文件路径: {file_path}\n\n", "filepath")
    result_text.config(state=tk.DISABLED)

    # 使用线程池并行计算哈希
    with concurrent.futures.ThreadPoolExecutor() as executor:
        futures = {}
        if crc32_var.get():
            futures["CRC32"] = executor.submit(calculate_crc32, file_path, update_progress)
        if md5_var.get():
            futures["MD5"] = executor.submit(calculate_hash, file_path, MD5, update_progress)
        if sha1_var.get():
            futures["SHA1"] = executor.submit(calculate_hash, file_path, SHA1, update_progress)
        if sha224_var.get():
            futures["SHA224"] = executor.submit(calculate_hash, file_path, SHA224, update_progress)
        if sha256_var.get():
            futures["SHA256"] = executor.submit(calculate_hash, file_path, SHA256, update_progress)
        if sha384_var.get():
            futures["SHA384"] = executor.submit(calculate_hash, file_path, SHA384, update_progress)
        if sha512_var.get():
            futures["SHA512"] = executor.submit(calculate_hash, file_path, SHA512, update_progress)

        # 显示哈希计算结果
        for label, future in futures.items():
            hash_result = future.result()
            insert_colored_text(result_text, label, hash_result)

def on_drop(event):
    file_path = event.data
    if file_path and file_path.startswith('{') and file_path.endswith('}'):
        file_path = file_path[1:-1]  # 去掉路径中的大括号
    file_path = file_path.strip()  # 去除路径中的多余空格和换行符

    # 创建并启动一个后台线程来计算哈希值
    threading.Thread(target=calculate_all_hashes, args=(file_path,)).start()

def init_checkboxes():
    global checkboxes  # 声明全局变量
    checkboxes = [
        (tk.Checkbutton(checkbox_frame, text="CRC32", variable=crc32_var), crc32_var),
        (tk.Checkbutton(checkbox_frame, text="MD5", variable=md5_var), md5_var),
        (tk.Checkbutton(checkbox_frame, text="SHA1", variable=sha1_var), sha1_var),
        (tk.Checkbutton(checkbox_frame, text="SHA224", variable=sha224_var), sha224_var),
        (tk.Checkbutton(checkbox_frame, text="SHA256", variable=sha256_var), sha256_var),
        (tk.Checkbutton(checkbox_frame, text="SHA384", variable=sha384_var), sha384_var),
        (tk.Checkbutton(checkbox_frame, text="SHA512", variable=sha512_var), sha512_var),
    ]

    # 将复选框排列在一行
    for idx, (checkbox, var) in enumerate(checkboxes):
        checkbox.grid(row=0, column=idx, padx=CHECKBOX_PADDING, sticky='w')

def adjust_checkboxes():
    frame_width = checkbox_frame.winfo_width()
    total_width = sum(checkbox.winfo_width() for checkbox, _ in checkboxes) + CHECKBOX_PADDING * (len(checkboxes) - 1)

    if total_width > frame_width:
        columns = frame_width // (checkboxes[0][0].winfo_width() + CHECKBOX_PADDING)
        if columns == 0:
            columns = 1
        rows = (len(checkboxes) + columns - 1) // columns
        for idx, (checkbox, var) in enumerate(checkboxes):
            checkbox.grid(row=idx // columns, column=idx % columns, padx=CHECKBOX_PADDING, pady=CHECKBOX_PADDING, sticky='w')
    else:
        for idx, (checkbox, var) in enumerate(checkboxes):
            checkbox.grid(row=0, column=idx, padx=CHECKBOX_PADDING, sticky='w')

# 创建主窗口
root = TkinterDnD.Tk()
root.title("文件哈希计算器")
root.geometry(MAIN_WINDOW_SIZE)

# 提示标签
label = tk.Label(root, text="请将文件拖放到此窗口", font=("Arial", 14))
label.grid(row=0, column=0, padx=20, pady=5, sticky="w")  # 减少了上下行距

# 复选框框架
checkbox_frame = tk.Frame(root)
checkbox_frame.grid(row=1, column=0, padx=20, pady=5, sticky="w")  # 减少了上下行距

# 复选框变量
crc32_var = tk.BooleanVar(value=True)
md5_var = tk.BooleanVar(value=True)
sha1_var = tk.BooleanVar(value=True)
sha224_var = tk.BooleanVar(value=True)
sha256_var = tk.BooleanVar(value=True)
sha384_var = tk.BooleanVar(value=True)
sha512_var = tk.BooleanVar(value=True)

# 初始化复选框
init_checkboxes()

# 结果显示框
result_text = tk.Text(root, wrap="word", font=("Arial", 12))
result_text.grid(row=3, column=0, padx=20, pady=5, sticky="nsew")
result_text.config(state=tk.DISABLED)
result_text.tag_configure("highlight", foreground="red")
result_text.tag_configure("normal", foreground="black")
result_text.tag_configure("filepath", foreground="blue", font=("Arial", 12, "bold"))

# 进度条
progress_bar = ttk.Progressbar(root, orient="horizontal", mode="determinate")
progress_bar.grid(row=4, column=0, padx=20, pady=5, sticky="ew")

# 设置拖放文件事件
root.drop_target_register(DND_FILES)
root.dnd_bind('<<Drop>>', on_drop)

# 设置窗口大小调整事件
root.bind("<Configure>", lambda e: adjust_checkboxes())

# 配置主窗口的行列权重
root.grid_rowconfigure(3, weight=1)  # 使结果显示框在窗口缩放时可扩展
root.grid_columnconfigure(0, weight=1)  # 使主列在窗口缩放时可扩展

root.mainloop()
